﻿using HAF.LogUtility;
using MediaTransfer.Model;
using System;
using System.IO;

namespace Iptv.UploadResourceItem.Helper
{
    /// <summary>
    /// 文件复制
    /// </summary>
    public class MediaReplication : PushMessageable, IDisposable
    {
        string _remoteFullPath = string.Empty;
        string _localFullPath = string.Empty;
        string _correlateId = string.Empty;
        string _lastErrMessage = string.Empty;
        /// <summary>
        /// 任务结束异步事件
        /// </summary>
        public Action<string, bool, string> UploadTaskEndEvent;
        /// <summary>
        /// 任务状态更新
        /// </summary>
        public Action<string, EnumUploadTaskStatus, int> UpdateUploadTaskStat;

        /// <summary>
        /// 析构函数
        /// </summary>
        public void Dispose()
        {
            ShowDebugMessage("任务[{0}]完成", _remoteFullPath);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="srcPath"></param>
        /// <param name="destPath"></param>
        public MediaReplication(string srcPath, string destPath)
        {
            _remoteFullPath = destPath;
            _localFullPath = srcPath;
            _lastErrMessage = "";
        }

        /// <summary>
        /// 启动文件复制
        /// </summary>
        /// <param name="correlateId"></param>
        public void Run(string correlateId)
        {
            WriteLine();
            _correlateId = correlateId;

            //第一步：目的盘存储目录是否存在
            string destDir = Path.GetDirectoryName(_remoteFullPath);
            if (!Directory.Exists(destDir))
            {
                ShowWarnMessage("目标目录{0}不存在，创建目录", destDir);
                Directory.CreateDirectory(destDir);
            }

            //第二步：复制源文件到目的目录
            bool result = FileCopy(_localFullPath, _remoteFullPath);
            UploadTaskEndEvent?.Invoke(correlateId, result, _lastErrMessage);

        }

        /// <summary>
        /// 文件复制
        /// </summary>
        /// <param name="source">源文件，例如：@"D:\media\复制.avi"</param>
        /// <param name="target">目标文件，例如：@"D:\media\复制.avi"</param>
        private bool FileCopy(string source, string target)
        {
            ShowDebugMessage("开始复制{0}到{1}", source, target);
            try
            {

                //思路：先将需要复制的文件读取出来，然后再写入到指定的位置
                //多线程的作用是用来提高CPU的潜力，也就是提高可并行任务状态下的CPU工作时间而已，在文件IO这里，根本不适合什么多线程，它对服务器负载并无用处。
                //创建一个读取的流
                using (FileStream fsRead = new FileStream(source, FileMode.Open, FileAccess.Read))
                {
                    //获取文件长度
                    long dataLength = fsRead.Length;
                    int percentage;
                    ShowInfoMessage("文件{0}大小为{1}", source, dataLength);
                    //如果文件存在，再次检查文件大小一致性，有重复任务
                    if (File.Exists(target))
                    {
                        FileInfo targetInfo = new FileInfo(target);
                        if (dataLength == targetInfo.Length)
                        {
                            ShowWarnMessage("文件复检大小一致，不再重复操作");
                            return true;
                        }
                    }
                    //创建一个写入的流  如果文件已存在将进行覆盖
                    using (FileStream fsWrite = new FileStream(target, FileMode.Create, FileAccess.Write))
                    {
                        long postion = fsRead.Position;
                        //启动流量计算对象
                        //FlowAssistantor timer = new FlowAssistantor();
                        //fsRead.Length;
                        //设置2M的缓存
                        //数据流量单位换算：1GB=1024MB，1MB=1024KB，1KB=1024B(Byte)，1B（字节）=8bits（比特）
                        byte[] buffer = new byte[1024 * 1024 * 2];
                        int readCount = 0;
                        int readLength = 0;
                        //r 本次实际读取到的字节数
                        while ((readCount = fsRead.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            //可在复制时同步进行MD5计算
                            fsWrite.Write(buffer, 0, readCount);
                            readLength += readCount;
                            //timer.ReadSize = readCount;
                            //计算已经读取的数据百分比 48234496 647910488
                            percentage = (int)((long)readLength * 100 / dataLength) ;
                            //if (percentage > 0 && percentage % 4 == 0)
                            //{
                            //    UpdateUploadTaskStat?.Invoke(_correlateId, EnumUploadTaskStatus.Exec, percentage);
                            //}
                            //else
                            //{
                            WriteDebugMessage("{0} {1} {2}", readLength, dataLength, percentage);
                            //}
                            //Debug.WriteLine(percentage);
                        }

                        //无限循环中反复读写，直到读完写完
                        //while (true)
                        //{
                        //    int readCount = fsr.Read(buffer, 0, buffer.Length);
                        //    fsw.Write(buffer, 0, readCount);
                        //    if (readCount < buffer.Length)
                        //    {
                        //        break;
                        //    }
                        //}
                        //md5 是每 512 bit 作为一个 chunk 进行计算的。所以可以每次读取一部分的内容（最少 512 bit，比较合适是 st_blksize），进行那些 chunk 部分的计算，之后再读取下一部分内容继续计算。
                    }
                }
                GC.Collect();
                return true;
            }
            catch (Exception ex)
            {
                ShowErrMessage(ex.ToString());
                _lastErrMessage = ex.Message;
                return false;
            }

        }
    }
}
